package com.jp.fm.service;

import static org.easymock.EasyMock.anyObject;
import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.expect;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import com.jp.fm.EasyMockParent;
import com.jp.fm.dao.AccountDao;
import com.jp.fm.dao.CategoryDao;
import com.jp.fm.dao.TransactionDao;
import com.jp.fm.data.Account;
import com.jp.fm.data.Category;
import com.jp.fm.data.Transaction;

public class TransactionServiceTest extends EasyMockParent {

	private TransactionService transactionService = null;
	private TransactionDao mockTransactionDao = null;
	private AccountDao mockAccountDao = null;
	private CategoryDao mockCategoryDao = null;
	private AccountDao accountDao = null;

	@BeforeClass
	public static void setUpBeforeClass() throws Exception {
	}

	@AfterClass
	public static void tearDownAfterClass() throws Exception {
	}

	@Before
	public void setUp() throws Exception {

		super.setUp();
		transactionService = new TransactionService();
		accountDao = new AccountDao();
	}

	@After
	public void tearDown() throws Exception {
		super.setUp();
	}

	@Test
	public final void testSearchTransactionsByDateAllAccountsSuccess() {
		transactionService.setTransactionDao(mockTransactionDao);
		transactionService.setAccountDao(mockAccountDao);

		List<Transaction> list = new ArrayList<Transaction>();
		list.add(new Transaction(null, null, null, null, null, null, null,
				false, null));

		List<Account> accts = new ArrayList<Account>();
		accts.add(new Account(null, "name", null, null, null, null, 0));

		Date startDate = new Date();
		Date endDate = new Date();

		expect(mockAccountDao.findAll()).andReturn(accts);

		expect(
				mockTransactionDao.findByAny(anyObject(Category.class),
						eq(startDate), eq(endDate), anyObject(String.class),
						anyObject(String.class), anyObject(Boolean.class)))
				.andReturn(list);

		replayMocks();

		List<Transaction> ret = transactionService.searchTransactionsByDate(
				startDate, endDate, "all");

		assertNotNull(ret);
		assertEquals(1, ret.size());
	}

	@Test
	public final void testSearchTransactionsByDateSuccess() {
		transactionService.setTransactionDao(mockTransactionDao);
		transactionService.setAccountDao(mockAccountDao);

		List<Transaction> list = new ArrayList<Transaction>();
		list.add(new Transaction(null, null, null, null, null, null, null,
				false, null));

		List<Account> accts = new ArrayList<Account>();
		accts.add(new Account(null, "test", null, null, null, null, 0));

		Date startDate = new Date();
		Date endDate = new Date();

		expect(mockAccountDao.findAll()).andReturn(accts);

		expect(
				mockTransactionDao.findByAny(anyObject(Category.class),
						eq(startDate), eq(endDate), eq("test"),
						anyObject(String.class), anyObject(Boolean.class)))
				.andReturn(list);

		replayMocks();

		List<Transaction> ret = transactionService.searchTransactionsByDate(
				startDate, endDate, "test");

		assertNotNull(ret);
		assertEquals(1, ret.size());
	}

	@Test
	public final void testFindUncategorizedTransactionsSuccess() {

		transactionService.setTransactionDao(mockTransactionDao);
		transactionService.setCategoryDao(mockCategoryDao);

		List<Transaction> list = new ArrayList<Transaction>();
		list.add(new Transaction(null, null, null, null, null, null, null,
				false, null));

		expect(mockCategoryDao.findByName("Unknown")).andReturn(
				new Category(null, "Unknown"));

		expect(
				mockTransactionDao.findByAny(anyObject(Category.class),
						anyObject(Date.class), anyObject(Date.class),
						anyObject(String.class), anyObject(String.class),
						anyObject(Boolean.class))).andReturn(list);

		replayMocks();

		List<String> ret = transactionService.findUncategorizedTransactions();

		assertNotNull(ret);
		assertEquals(1, ret.size());
	}

	@Test
	public final void testFindFeeTransactionsSuccess() {

		transactionService.setTransactionDao(mockTransactionDao);
		transactionService.setCategoryDao(mockCategoryDao);

		List<Transaction> list = new ArrayList<Transaction>();
		list.add(new Transaction(null, null, null, Float.valueOf("10.99"),
				null, null, null, false, null));

		expect(mockCategoryDao.findByName("Fees")).andReturn(
				new Category(null, "Fees"));

		expect(
				mockTransactionDao.findByAny(anyObject(Category.class),
						anyObject(Date.class), anyObject(Date.class),
						anyObject(String.class), anyObject(String.class),
						anyObject(Boolean.class))).andReturn(list);

		replayMocks();

		List<String> ret = transactionService.findFeeTransactions();

		assertNotNull(ret);
		assertEquals(1, ret.size());
	}

	@Test
	public final void testFindUnconfirmedTransactionsSuccess() {

		transactionService.setTransactionDao(mockTransactionDao);

		List<Transaction> list = new ArrayList<Transaction>();
		list.add(new Transaction(null, null, null, null, null, null, null,
				false, null));

		expect(
				mockTransactionDao.findByAny(null, null, null, null, null,
						Boolean.FALSE)).andReturn(list);

		replayMocks();

		List<String> ret = transactionService.findUnconfirmedTransactions();

		assertNotNull(ret);
		assertEquals(1, ret.size());
	}

	@Test
	public final void testFindUnconfirmedTransactionsNullReturn() {

		transactionService.setTransactionDao(mockTransactionDao);

		expect(
				mockTransactionDao.findByAny(null, null, null, null, null,
						Boolean.FALSE)).andReturn(null);

		replayMocks();

		List<String> ret = transactionService.findUnconfirmedTransactions();

		assertNotNull(ret);
		assertEquals(0, ret.size());
	}

	@Test
	public final void testCalculateTransactionsTotalOnlyParentTransactions() {

		List<Transaction> trans = new ArrayList<Transaction>();
		Transaction tran = new Transaction(new Date(), null,
				"Checking Account", Float.valueOf("-10.99"), new Category(null,
						"Gas"), "", "", true, null);
		Transaction tran2 = new Transaction(new Date(), null, "Amex",
				Float.valueOf("10.00"), new Category(null, "Household"), "",
				"", true, null);

		trans.add(tran);
		trans.add(tran2);
		transactionService.setAccountDao(accountDao);
		Float amt = transactionService.calculateTransactionsTotal(trans);

		assertNotNull(amt);
		assertEquals(Float.valueOf("20.99"), amt);
	}

	@Test
	public final void testCalculateTransactionsTotalParentAndChildWithOneCategory() {

		List<Transaction> trans = new ArrayList<Transaction>();
		Transaction tran = new Transaction(new Date(), 0, "Amex",
				Float.valueOf("3.00"), new Category(null, "Gas"), "", "", true,
				null);

		List<Transaction> comps = tran.getCompositeTransactions();
		Transaction tran2 = new Transaction(new Date(), 1, "Amex",
				Float.valueOf("1.00"), new Category(null, "Household"), "", "",
				true, 0);
		Transaction tran3 = new Transaction(new Date(), 1, "Amex",
				Float.valueOf("2.00"), new Category(null, "test2"), "", "",
				true, 0);

		trans.add(tran);
		comps.add(tran2);
		comps.add(tran3);
		transactionService.setAccountDao(accountDao);
		Float amt = transactionService.calculateTransactionsTotal(trans,
				new Category(null, "Household"));

		assertNotNull(amt);
		assertEquals(Float.valueOf("1.00"), amt);

	}

	@Test
	public final void testCalculateTransactionsTotalParentAndChildWithOneCategoryParentMatch() {

		List<Transaction> trans = new ArrayList<Transaction>();
		Transaction tran = new Transaction(new Date(), 0, "Amex",
				Float.valueOf("3.00"), new Category(null, "Gas"), "", "", true,
				null);

		List<Transaction> comps = tran.getCompositeTransactions();
		Transaction tran2 = new Transaction(new Date(), 1, "Amex",
				Float.valueOf("1.00"), new Category(null, "Household"), "", "",
				true, 0);
		Transaction tran3 = new Transaction(new Date(), 1, "Amex",
				Float.valueOf("2.00"), new Category(null, "test2"), "", "",
				true, 0);

		trans.add(tran);
		comps.add(tran2);
		comps.add(tran3);
		transactionService.setAccountDao(accountDao);
		Float amt = transactionService.calculateTransactionsTotal(trans,
				new Category(null, "Gas"));

		assertNotNull(amt);
		assertEquals(Float.valueOf("3.00"), amt);
	}

	@Test
	public final void testCalculateTransactionsTotalParentAndChildWithOneCategoryButNoMatch() {

		List<Transaction> trans = new ArrayList<Transaction>();
		Transaction tran = new Transaction(new Date(), 0, "Amex",
				Float.valueOf("3.00"), new Category(null, "Gas"), "", "", true,
				null);

		List<Transaction> comps = tran.getCompositeTransactions();
		Transaction tran2 = new Transaction(new Date(), 1, "Amex",
				Float.valueOf("1.00"), new Category(null, "Household"), "", "",
				true, 0);
		Transaction tran3 = new Transaction(new Date(), 1, "Amex",
				Float.valueOf("2.00"), new Category(null, "test2"), "", "",
				true, 0);

		trans.add(tran);
		comps.add(tran2);
		comps.add(tran3);
		transactionService.setAccountDao(accountDao);
		Float amt = transactionService.calculateTransactionsTotal(trans,
				new Category(null, "test3"));

		assertNotNull(amt);
		assertEquals(Float.valueOf("0.00"), amt);
	}

	@Test
	public final void testCalculateTransactionsTotalParentAndChildWithMultipleCategories() {
		List<Transaction> trans = new ArrayList<Transaction>();
		Transaction tran = new Transaction(new Date(), 0, "Amex",
				Float.valueOf("3.00"), new Category(null, "Gas"), "", "", true,
				null);

		List<Transaction> comps = tran.getCompositeTransactions();
		Transaction tran2 = new Transaction(new Date(), 1, "Amex",
				Float.valueOf("1.00"), new Category(null, "Household"), "", "",
				true, 0);
		Transaction tran3 = new Transaction(new Date(), 1, "Amex",
				Float.valueOf("2.00"), new Category(null, "test2"), "", "",
				true, 0);

		trans.add(tran);
		comps.add(tran2);
		comps.add(tran3);

		List<Category> cats = new ArrayList<Category>();
		cats.add(new Category(null, "Household"));
		cats.add(new Category(null, "test2"));

		transactionService.setAccountDao(accountDao);
		Float amt = transactionService.calculateTransactionsTotal(trans, cats);

		assertNotNull(amt);
		assertEquals(Float.valueOf("3.00"), amt);
	}

	@Test
	public final void testCalculateTransactionsTotalParentAndChildWithMultipleCategoriesParentMatch() {
		List<Transaction> trans = new ArrayList<Transaction>();
		Transaction tran = new Transaction(new Date(), 0, "Amex",
				Float.valueOf("3.00"), new Category(null, "Gas"), "", "", true,
				null);

		List<Transaction> comps = tran.getCompositeTransactions();
		Transaction tran2 = new Transaction(new Date(), 1, "Amex",
				Float.valueOf("1.00"), new Category(null, "Household"), "", "",
				true, 0);
		Transaction tran3 = new Transaction(new Date(), 1, "Amex",
				Float.valueOf("2.00"), new Category(null, "test2"), "", "",
				true, 0);

		trans.add(tran);
		comps.add(tran2);
		comps.add(tran3);

		List<Category> cats = new ArrayList<Category>();
		cats.add(new Category(null, "Household"));
		cats.add(new Category(null, "Gas"));
		transactionService.setAccountDao(accountDao);
		Float amt = transactionService.calculateTransactionsTotal(trans, cats);

		assertNotNull(amt);
		assertEquals(Float.valueOf("3.00"), amt);
	}
}
